package ssm.controller;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import ssm.domain.SysLog;
import ssm.service.ISysLogService;

import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Date;

@Component
@Aspect
/*上面两个注解表示该处为非dao/service/controller的控制；
* 同时是代表aspect切面操作*/
public class LogAOP {
    /*用于获取一个事务操作过程中的操作信息*/

    @Autowired
    /*此处引入该变量，是为了便于通过request请求获取其中的信息*/
    private HttpServletRequest request;
    @Autowired
    private ISysLogService sysLogService;

    private Date visitTime;
    private Class clazz;
    private Method method;

    //前置通知: 用于获取当前访问时间、执行的类与方法
    @Before("execution(* ssm.controller.*.*(..))")
    /*参数为某个连接点*/
    public void doBefore(JoinPoint jp) throws NoSuchMethodException {
        visitTime = new Date();     //即访问时间
        clazz = jp.getTarget().getClass();      //获取要访问的类

        /*确定要访问的方法名称*/
        String methodName = jp.getSignature().getName();

        /*利用反射，根据类与方法名，生成method对象,但获取的仅为无参数方法*/
        Object[] args = jp.getArgs();   //获取jp是否有参数
        if(args == null || args.length == 0){
            /*表示为无参方法*/
            method =clazz.getMethod(methodName);
        }else{
            /*1、对于有参数的情况，先创建一个和参数一致长度的class数组*/
            Class[] classArgs = new Class[args.length];
            /*2.将每一个对象，生成对象，放入到classArgs中*/
            for (int i = 0; i < args.length; i++) {
                classArgs[i] = args[i].getClass();
            }
            clazz.getMethod(methodName,classArgs);
        }
    }

    //后置通知
    @After("execution(* ssm.controller.*.*(..))")
    /*参数为某个连接点*/
    public void doAfter(JoinPoint jp) throws Exception {
        /*1.获取访问时长:获取到各自的时间值，做减法即可*/
        long time = new Date().getTime() - visitTime.getTime();

        String url = "";

        /*2.获取操作的url*/
                /*1,先判断操作的对象不为空，且不为当前的对象，否则无意义*/
        if(clazz != null && method != null && clazz != LogAOP.class){

            /*先获取注解RequestMapping的值*/
            RequestMapping annotation =(RequestMapping) clazz.getAnnotation(RequestMapping.class);
            if(annotation != null){
                String[] annotationValue = annotation.value();

                /*获取访问方法的RequestMapping路径*/
                RequestMapping methodAnnotation = method.getAnnotation(RequestMapping.class);
                if (methodAnnotation != null){
                    String[] methodValue = methodAnnotation.value();

                    /*将注解的路径名+方法的路径名即为访问路径*/
                    url = annotationValue[0] + methodValue[0];

                    /*3.获取访问的IP地址:配置RequestContextListener监听器；然后注入request变量；通过request获取远程的地址，即ip*/
                    String ip = request.getRemoteAddr();

                    /*4.获取当前操作用户*/
                    SecurityContext context = SecurityContextHolder.getContext();   //通过权限控制中的方法，获取上下文
                    User user = (User) context.getAuthentication().getPrincipal();
                    String username = user.getUsername();

                    /*将我们获取的关于日志相关信息，封装到SysLog类中*/
                    SysLog sysLog = new SysLog();
                    sysLog.setExecutionTime(time);
                    sysLog.setIp(ip);
                    sysLog.setUrl(url);
                    sysLog.setUsername(username);
                    sysLog.setVisitTime(visitTime);
                    sysLog.setMethod("[类名] "+clazz.getName()+"[方法名] "+method.getName());


                    /*在service层中完成添加日志操作*/
                    if(!sysLog.getUrl().equals("/sysLog/findAll.do") ){
                        sysLogService.save(sysLog);
                    }
                }
            }
        }


    }

}
